import type {
  SimpleWorkdayInfo,
  WorkdayInfo
} from '@/components/chart/bar-line-scatter-chart/util/AxiosReqBuilder'
import { trim } from 'lodash-es'
import {
  buildDayjs,
  dateConvert,
  DateTimeFormatStyleEnum,
  getWeekDay,
  isFutureDate
} from './DateUtil'

/**
 * # 判断val是否为null或undefined
 * @param val 待验证数据
 * @returns
 */
export function isNullOrUndefined<T>(val: T) {
  return val === null || val === undefined
}
/**
 * # 判断val是否为null或undefined或空字符串
 * ---
 * 纯空格组成的字符串也会被判定为空字符串
 *
 * @param val 待验证数据
 * @returns
 */
export function isNullOrUndefinedOrEmptyStr<T>(val: T) {
  let ret = isNullOrUndefined(val)
  if (!ret) {
    if (typeof val === 'string' && trim(val) === '') {
      ret = true
    }
  }
  return ret
}
/**
 * # 将对象数组转换为以fieldName为key的map对象
 * @param objArr 待转换的对象数组
 * @param fieldName 作为map的key的字段
 * @returns
 */
export function arrayToMapByFieldValue<T extends Object>(objArr: T[], fieldName: string) {
  const map = objArr.reduce((map, cur) => {
    // @ts-ignore
    const key = cur[fieldName]
    map.set(key, cur)
    return map
  }, new Map<string | number | null | undefined, T>())
  return map
}
/**
 * # 判断x是否为数值或字符串类型
 * @param x 待验证数据
 * @returns
 */
export function isNumberOrString<T>(x: T) {
  return typeof x === 'string' || typeof x === 'number'
}
/**
 * # 字段填充策略
 */
export enum FitValStrategyEnum {
  /**
   * # 未来日期数据补null,过去日期数据补0
   * ---
   * 过去日期包含当前日期
   */
  FUTURE_FIT_NULL_OTHER_FIT_ZERO = 'FUTURE_FIT_NULL_OTHER_FIT_ZERO'
}
/**
 * # 基于基准数据中某个字段的数据补全
 * @param standardDataArr 基准数据
 * @param targetDataArr 待补全数据
 * @param standardDataCompareField 基准数据中用于对比的字段
 * @param targetDataCompareField 待补全数据中用于对比的字段
 * @param completionFieldArr 需要补全数据的字段名
 * @param fitVal 补全的数据值
 * @param curDate 当前日期. 如果未设置则取当前操作系统的日期时间. 值格式: YYYYMMDD
 * @returns
 */
export function dataCompletion<S extends Object, T extends Object>(
  standardDataArr: S[],
  targetDataArr: T[],
  standardDataCompareField: string,
  targetDataCompareField: string,
  completionFieldArr: string[],
  fitVal:
    | string
    | number
    | null
    | FitValStrategyEnum
    | ((t: T, fieldName: string) => string | number | null),
  curDate?: string
) {
  const tmpTargetDataArr = targetDataArr.map((item) => {
    const obj = {
      ...item
    }
    // 将日期字段转换为数值，避免格式不一致，造成无法匹配
    // @ts-ignore
    obj[targetDataCompareField] = dateConvert(obj[targetDataCompareField])
    return obj
  })
  const targetDataMap = arrayToMapByFieldValue<T>(tmpTargetDataArr, targetDataCompareField)
  const retArr: T[] = []
  // 遍历基准数据，以基准数据来补全目标数据
  standardDataArr.forEach((item) => {
    // 获取到日期(这里必须保证是个数字值)
    // @ts-ignore
    const key = item[standardDataCompareField]
    if (!isNullOrUndefined(key)) {
      let obj = targetDataMap.get(key)
      if (isNullOrUndefined(obj)) {
        // @ts-ignore
        obj = {}
        // @ts-ignore
        obj[targetDataCompareField] = key
      }
      // 字段值填充
      completionFieldArr.forEach((fieldName) => {
        // @ts-ignore
        if (isNullOrUndefinedOrEmptyStr(obj[fieldName])) {
          let val = null
          if (typeof fitVal === 'function') {
            // @ts-ignore
            val = fitVal(obj, fieldName)
          } else if (typeof fitVal === 'number') {
            val = fitVal
          } else if (fitVal === FitValStrategyEnum.FUTURE_FIT_NULL_OTHER_FIT_ZERO) {
            // 未来数据补null，过去和当前数据补0
            if (isFutureDate(`${key}`, curDate, DateTimeFormatStyleEnum.YYYYMMDD)) {
              val = null
            } else {
              val = 0
            }
          }
          // @ts-ignore
          obj[fieldName] = val
        }
      })
      // @ts-ignore
      retArr.push(obj)
    }
  })
  return retArr
}

/**
 * # 如果num表示的数字小于0，则添加0作为前缀
 * ---
 * 如: 1 --> 01, 10 --> 10, 100--> 100
 * @param num 待添加0作为前缀的数据
 * @returns 补0后的数字
 */
export function fillPrefixZero(num: number | string) {
  const tmpNum = Number(`${num}`)
  if (tmpNum < 10) {
    return `0${tmpNum}`
  } else {
    return `${tmpNum}`
  }
}
/**
 * 将简单的工作日信息，转换为完备的工作日信息
 *
 * @param workDataArr 工作日信息
 * @param curDate 当前日期. 如果未指定，则为当前操作系统的日期时间
 */
export function simpleWorkdayToWorkdayInfo(workDataArr: SimpleWorkdayInfo[], curDate?: string) {
  const tmpArr = workDataArr ?? []
  const curDj = buildDayjs(curDate, DateTimeFormatStyleEnum.YYYYMMDD)
  const retArr = tmpArr.map((item) => {
    const { year, month, week, date, isWorkDay } = item
    const dateNum = dateConvert(date)
    const dateStr = `${dateNum}`
    const weekDay = getWeekDay(dateStr, DateTimeFormatStyleEnum.YYYYMMDD)
    const yearNum = parseInt(`${year}`)
    const monthNum = parseInt(`${month}`)
    const monthStr = fillPrefixZero(monthNum)
    const weekNum = parseInt(`${week}`)
    const weekStr = fillPrefixZero(weekNum)
    const yearMonth = parseInt(`${year}${monthStr}`)
    const yearWeek = parseInt(`${year}${weekStr}`)
    const dj = buildDayjs(`${dateNum}`, DateTimeFormatStyleEnum.YYYYMMDD)
    const _isFutureDate = isFutureDate(`${dateNum}`, curDj, DateTimeFormatStyleEnum.YYYYMMDD)
    const ret: WorkdayInfo = {
      year: yearNum,
      month: monthNum,
      week: weekNum,
      day: dj.get('date'),
      date: dateNum as number,
      yearMonth,
      yearWeek,
      isFuture: _isFutureDate ? 1 : 0,
      isWorkDay,
      weekDay
    }
    return ret
  })
  return retArr
}
